
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

using Swaf.Container;

namespace Swaf.BizObj
{	
	/// <summary>
	/// Defines how to access, manipulate, and query the properties of application 
	/// business objects.</summary>
	/// <remarks>
	/// This is the core interface for accessing business object properties.  Information
	/// about a specific type of application business object can be discovered via
	/// this interface.</remarks>
	/// <author>mdenman</author>
	public interface IBizObj : INameAddressSupport, ICloneable
	{
		/// <summary>Returns the name of this <code>BusinessObject</code>'s type.
		/// For example, returns "Address" if this BusinessObject is of type Address.
		/// </summary>
		IBizObjDefinition type{get;}

		/// <summary> Returns true if this <code>BusinessObject</code> is of the specified business
		/// object type.</summary>
		/// <remarks>
		/// When a <code>BusinessObject</code> definition specifies a
		/// parent class through the extends attribute it not only inherits all of
		/// the properties specified in the parent class, it also is a kind of
		/// the parent <code>BusinessObject</code></remarks>
		/// <param name="typeName">A valid business object type.</param>
		bool isKindOf(string typeName);

		/// <summary>
		/// Checks if <c>this</c> bizobj has had the given field set.
		/// </summary>
		/// <param name="name">The name of the field to set.  Support the "name address" technology.</param>
		/// <returns></returns>
		bool isFieldSet(string name);

		/// <summary>
		/// Removes any value that was put into the field, which results in calls to get returning the
		/// field definition's default value.
		/// </summary>
		/// <param name="name"></param>
		void unsetField(string name);

		/// <summary>
		/// Checks if <code>this</code> <code>BusinessObject</code> contains the specified field.</summary>
		/// <remarks>
		/// <p>A call of the form <code>obj.containsField("a.b")</code> is equivalent to
		/// <code>obj.get("a").containsField("b")</code> (ignoring the typecast that is
		/// needed to make the latter compile cleanly).  Any number of dots are
		/// allowed; e.g.,
		/// <code>aPatient.containsField("PrimaryCarePhysician.Father.Address.Street1")</code>
		/// checks if "Street1" exists in a patient's doctor's father's address.
		/// It returns <code>false</code> if any name in the chain is invalid.
		/// </p></remarks>
		/// <param name="name">the name of the field.</param>
		/// <returns><code>true</code> if and only if <code>name</code> is a valid field name to be
		/// passed to <code>this.get()</code>, <code>this.put()</code>, <code>this.putValidated()</code>, etc.
		/// </returns>
		bool containsField(string name);

		object getItem(string name, params object[] p);

		/// <summary>Changes the value of the specified field of <code>this</code> <code>BusinessObject</code>,
		/// first performing all validity checks.</summary>
		/// <remarks>
		/// <p>A call of the form <code>obj.putValidated("a.b",value)</code> is equivalent to
		/// <code>obj.get("a").putValidated("b")</code> (ignoring the typecast that is
		/// needed to make the latter compile cleanly).  Any number of dots are
		/// allowed; e.g.,
		/// <code>aPatient.putValidated("PrimaryCarePhysician.Father.Address.Street1",value)</code>
		/// sets a patient's doctor's father's address's street.
		/// If any name in the chain is invalid, throws a <code>BadField</code> exception.
		/// </p><p>
		/// <b>Preconditions</b> (<em>important:</em> these preconditions are checked immediately;
		/// see <code>put()</code> if you want to defer these checks until you call
		/// <code>this.validate()</code>):
		/// <ul>
		/// <li><code>name</code> must be a field of <code>this</code> <code>BusinessObject</code>.
		/// That is, <code>this.containsField(name)</code> must return <code>true</code>.</li>
		/// 
		/// <li><code>value</code> must not be <code>null</code>.</li>
		/// 
		/// <li>the type of <code>value</code> must either be what is expected for the
		/// specified field, or <code>String</code>.  For example, if <code>this</code>
		/// <code>BusinessObject</code>'s <code>Age</code> field is supposed to be of type
		/// <code>Integer</code>, <code>value</code> must either be an <code>Integer</code> or a
		/// <code>String</code>.  It must not be, for example, a <code>IList</code>.</li>
		/// 
		/// <li>if <code>value</code> is of type <code>String</code>, it must be valid according
		/// to the field's "validator."  For example, if there is a <code>ZipCode</code>
		/// field in the XML that defines <code>this</code> <code>BusinessObject</code>,
		/// and if that field's <code>validator=</code> specification contains
		/// <code>"ZipPlusOpt4"</code>, then calling <code>this.putValidated("ZipCode","xyzzy")</code>
		/// will be illegal (assuming the <code>ZipPlusOpt4</code> validator rejects the
		/// string <code>"xyzzy"</code>).</li>
		/// 
		/// <li>if the type of <code>value</code> is supposed to be some aggregate type
		/// (such as <code>BusinessObject</code> or <code>IList</code>), <code>value</code> must <em>not</em>
		/// be a <code>String</code>.  For example, if the <code>PrimaryAddress</code> field
		/// of <code>this</code> <code>BusinessObject</code> is supposed to be of type
		/// <code>Address</code> (that is, if the XML that defines the <code>PrimaryAddress</code>
		/// field of <code>this</code> <code>BusinessObject</code> says <code>type=Address</code>),
		/// then (assuming <code>Address</code> is the <code>type()</code> of some <code>BusinessObject</code>),
		/// then it is illegal to say <code>this.putValidated("PrimaryAddress","aString")</code>.</li>
		/// 
		/// <li>if the type of <code>value</code> is supposed to be some kind of
		/// <code>BusinessObject</code>, <code>value</code> must be a <code>BusinessObject</code>
		/// of the right type.  For example, if the <code>PrimaryAddress</code> field
		/// of <code>this</code> <code>BusinessObject</code> is supposed to be of type
		/// <code>Address</code> (assuming <code>Address</code> is the <code>type()</code> of some
		/// <code>BusinessObject</code>), then <code>((BusinessObject)value).type()</code> must
		/// return <code>"Address"</code>.</li>
		/// </ul>
		/// </p><p>
		/// <b>Postconditions</b> (<em>important:</em> unlike in <code>put()</code>,
		/// but like normal postconditions, these postconditions will be provided
		/// if and only if the preconditions are met):
		/// <ul>
		/// <li><code>this.get(name)</code> will return <code>value</code>.</li>
		/// <li><code>this.dirtyFields().contains(name)</code> will return <code>true</code>.</li>
		/// </ul></p>
		/// </remarks>
		/// <param name="name">The name of the field whose value is being changed.</param>
		/// <param name="newValue">The new value for the field.</param>
		void  putValidated(string name, object newValue);

		/// <summary>Validates the fields that have been changed via <code>put()</code>, but have not
		/// yet been successfully validated by a subsequent call to either this method or
		/// <code>this.putValidated()</code>.</summary>
		/// <example>
		/// IList errors = bizObj.validate();
		/// if (errors != null)
		/// {
		///		System.Console.WriteLine("No errors in this business object!");
		/// }
		/// else
		/// {
		///		System.Console.WriteLine("Found {0} error(s):", errors.size());
		///		foreach(BusinessObject.BadField e in errors)
		///		{
		///			System.Console.WriteLine("   field-name:   " + e.fieldName());
		///			System.Console.WriteLine("      biz-obj-type:  " + e.bizObjType());
		///			System.Console.WriteLine("      just-the-meat: " + e.getMessageLastPart());
		///			System.Console.WriteLine("      whole-thing-1: " + e.getMessage());
		///			System.Console.WriteLine("      whole-thing-2: " + e.toString());
		///			System.Console.WriteLine("      whole-thing-3: " + e);
		///		}
		/// }
		/// </example>
		/// <returns><code>null</code> if there were no invalid fields, otherwise returns
		/// a <code>IList</code> containing a <code>BusinessObject.BadField</code> object for
		/// each field-name in <code>undoableFields()</code> that had errors.
		/// Each of these <code>BusinessObject.BadField</code> objects describes the specifics
		/// of the problem with its corresponding field.  See <code>BadField</code>.
		/// </returns>
		List<ValidationError> validate();

		/// <summary>
		/// Checks if <code>this</code> <code>BusinessObject</code> is "dirty" (that is, if any field
		/// has been changed).</summary>
		/// <returns>
		/// Returns <code>true</code> if and only if <code>this.dirtyFields().size()</code> is non-zero.
		/// <p>Note: this (redundant) method exists simply because it is faster than calling
		/// <code>this.dirtyFields().size()</code>.</p>
		/// </returns>
		bool isDirty();

		/// <summary>
		/// Checks if the specified field is "dirty" (that is, if it has been changed).</summary>
		/// <remarks>
		/// <p>A call of the form <code>obj.isDirty("a.b")</code> is equivalent to
		/// <code>obj.get("a").isDirty("b")</code> (ignoring the typecast that is
		/// needed to make the latter compile cleanly).  Any number of dots are
		/// allowed; e.g.,
		/// <code>aPatient.isDirty("PrimaryCarePhysician.Father.Address.Street1")</code>
		/// checks if a patient's doctor's father's address's street is dirty.
		/// If any name in the chain is invalid, throws a <code>BadField</code> exception.
		/// </p><p>
		/// For those fields that are actually in <code>this</code> business object
		/// (i.e., for those fields that don't have a <code>"."</code> in the field name),
		/// returns <code>true</code> if and only if <code>this.dirtyFields().contains(name)</code>
		/// is <code>true</code>.</p></remarks>
		/// <param name="name">the name of the field that is being tested to see if it is dirty.</param>
		bool isDirty(string name);

		/// <summary>
		/// Checks if <code>this</code> <code>BusinessObject</code> is new.  </summary>
		/// <returns>
		/// By default, a newly created bizObj will always
		/// have this flag set to true.  If it is being populated by some external source for an existing bizObj, it should
		/// specifically be set to false by that process.
		/// </returns>
		bool isNew{get;set;}

		/// <summary>
		/// Checks if <code>this</code> <code>BusinessObject</code> has been flagged for deletion.  </summary>
		bool isDeleted{get;set;}

		/// <summary>
		/// Causes <code>isDirty()</code> to return <code>false</code>.</summary>
		void  clearDirtyFlags();
		void setAllFieldsDirty();
		void setDirtyFlag(string fieldName, bool val);

		/// <summary>
		/// Checks if the given field name and value are valid for <code>this</code> <code>BusinessObject</code>.
		/// If it is valid, returns <code>null</code>; otherwise returns a <code>BusinessObject.BadField</code> object
		/// that can be used to find out what was wrong.</summary>
		/// <remarks>
		/// <p>A call of the form <code>obj.validateField("a.b",value)</code> is equivalent to
		/// <code>obj.get("a").validateField("b",value)</code> (ignoring the typecast that is
		/// needed to make the latter compile cleanly).  Any number of dots are
		/// allowed; e.g.,
		/// <code>aPatient.validateField("PrimaryCarePhysician.Father.Address.Street1",value)</code>
		/// validates a patient's doctor's father's address's street.
		/// If any name in the chain is invalid, throws a <code>BadField</code> exception.
		/// </p><p>
		/// Here is what it means for <code>name</code> and <code>value</code> to be valid:
		/// <ul>
		/// <li><code>name</code> must cause <code>this.containsField(name)</code> to return <code>true</code>.</li>
		/// <li><code>value</code> must not be <code>null</code>.</li>
		/// <li>the type of <code>value</code> must either be what is expected for the
		/// specified field, or <code>String</code>.  For example, if <code>this</code>
		/// <code>BusinessObject</code>'s <code>Age</code> field is supposed to be of type
		/// <code>Integer</code>, <code>value</code> must either be an <code>Integer</code> or a
		/// <code>String</code>.  It must not be, for example, a <code>IList</code>.</li>
		/// 
		/// <li>if <code>value</code> is of type <code>String</code>, it must be valid according
		/// to the field's "validator."  For example, if there is a <code>ZipCode</code>
		/// field in the XML that defines <code>this</code> <code>BusinessObject</code>,
		/// and if that field's <code>validator=</code> specification contains
		/// <code>"ZipPlusOpt4"</code>, then calling <code>this.validateField("ZipCode","xyzzy")</code>
		/// will return a non-<code>null</code> value (assuming the <code>ZipPlusOpt4</code> validator rejects the
		/// string <code>"xyzzy"</code>).</li>
		/// 
		/// <li>if the type of <code>value</code> is supposed to be some aggregate type
		/// (such as <code>BusinessObject</code> or <code>IList</code>), <code>value</code> must <em>not</em>
		/// be a <code>String</code>.  For example, if the <code>PrimaryAddress</code> field
		/// of <code>this</code> <code>BusinessObject</code> is supposed to be of type
		/// <code>Address</code> (that is, if the XML that defines the <code>PrimaryAddress</code>
		/// field of <code>this</code> <code>BusinessObject</code> says <code>type=Address</code>),
		/// then (assuming <code>Address</code> is the <code>type()</code> of some <code>BusinessObject</code>),
		/// then <code>this.checkField("PrimaryAddress","aString")</code> will return a
		/// non-<code>null</code> value.</li>
		/// 
		/// <li>if the type of <code>value</code> is supposed to be some kind of
		/// <code>BusinessObject</code>, <code>value</code> must be a <code>BusinessObject</code>
		/// of the right type.  For example, if the <code>PrimaryAddress</code> field
		/// of <code>this</code> <code>BusinessObject</code> is supposed to be of type
		/// <code>Address</code> (assuming <code>Address</code> is the <code>type()</code> of some
		/// <code>BusinessObject</code>), then <code>((BusinessObject)value).type()</code> must
		/// return <code>"Address"</code>.</li>
		/// </ul></p>
		/// </remarks>
		/// <param name="name">The name of the field whose value is being changed.</param>
		/// <param name="newValue">The new value for the field.</param>
		BadBizObjFieldException validateField(string name, object newValue);

		void setFieldValidated(string name, bool state);
		/// <summary>
		/// Swaps everything about the two business objects.
		/// E.g., if one was an Apple and the other was a
		/// Bicycle, they'll be totally reversed at the end.
		/// </summary>
		void  swap(IBizObj that);

		string getXml(string name, bool setOnly, bool dirtyOnly, bool newOnly, bool getBasic, bool getObjs, bool getILists, bool getSysVals, XmlTextWriter xmlOut);
		string getXml(string name, string decoInfo, bool setOnly, bool dirtyOnly, bool newOnly, bool getBasic, bool getObjs, bool getILists, bool getSysVals, XmlTextWriter xmlOut);

		object KeyValue {get;}

		/// <summary>
		/// When getting bizobjs out of a bizobj list or through a bizobj cursor and you want to hold onto that, the variable should hold onto a reference.
		/// </summary>

		IBizObj Reference {get;}

		IDecorator getFieldDecoration(string fieldName, string decoratorName);
	}
}